#include "collision.h"

#include <limits>
#include <cmath>

namespace collision
{	
	collision_info_t test_collision(const ray3f ray, const sphere3f sphere)
	{
		/*
			sphere equation:
				x^2 + y^2 + z^2 - R^2 = 0;

			point within ray that intersects sphere:
				x = st + v; let's find t

			(sx*t+vx)^2 + (sy*t+vy)^2 + (sz*t+vz)^2 - R^2 = 0
			solve quadratic equation
		*/

		vec3f start = ray.origin - sphere.origin;
		vec3f dir = ray.dir;

		float a = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
		float b = 2 * dir.x * start.x + 2 * dir.y * start.y + 2 * dir.z * start.z;
		float c = start.x * start.x + start.y * start.y + start.z * start.z - sphere.R * sphere.R;
	
		float det = b*b - 4.f*a*c;

		float t1 = -1.f;
		float t2 = -1.f;

		bool is_real_values = det >= 0;
		if (is_real_values) 
		{
			float sroot_det = sqrt(det);
			t1 = (-b - sroot_det) / (2.f * a);
			t2 = (-b + sroot_det) / (2.f * a);
		}

		collision_info_t collision_info = make_miss_collinfo();

		if(is_real_values)
		{

			// if t1 is less than zero, the object is in the ray's negative dir
			// and consequently the ray misses the sphere
			//is_intersected = t1 >= 0.f;

			float t = FLT_MAX;

			if (t2 > 0)
			{
				if (t1 < 0) 
				{
					collision_info.status = collision_status::inside;
					t = t2;
				}
				else
				{
					collision_info.status = collision_status::hit;
					t = t1;
				}
			}

			vec3f p = start + dir * t;

			collision_info.pos = sphere.origin + p;
			collision_info.normal = normalize(p);
			collision_info.t = t;
		}
	
		return collision_info;
	}

	collision_info_t test_collision(const ray3f ray, const plane3f plane)
	{
		/*
			plane equation:
				dot(n,x)+d = 0
			point within ray that intersects plane:
				x = st+v; let's find t

			dot(n,st+v) + d = 0
			dot(n,st)+dot(n,v) = -d
			t*dot(n,s)+dot(n,v) = -d
			t = -(d + dot(n,v)) / dot(n,s)
		*/

		float nom = -(dot(plane.normal, ray.origin - origin()) + plane.D);
		float denom = dot(plane.normal, ray.dir);
		float t = nom / denom;
		collision_info_t collision_info;
	
		collision_info.pos = ray.origin + t * ray.dir;
		collision_info.normal = plane.normal;
		collision_info.uv = vec2fc(FLT_MAX, FLT_MAX);
		collision_info.t = t;
		collision_info.user_data = nullptr;
		collision_info.status = t > 0 ? collision_status::hit : collision_status::miss;

		return collision_info;
	}
}